package org.rowan.infovis.soccer;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import com.sun.opengl.util.*;
import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureCoords;
import java.awt.event.*;
import javax.swing.*;
import java.io.File;
import java.awt.BorderLayout;
import com.sun.opengl.util.texture.TextureIO;

public class SoccerVisualization implements GLEventListener, ActionListener, MouseListener, MouseMotionListener {
    //The name of the program (Window Title)
    private static String NAME = "Soccer Visualization";
    //The width of the window
    private int WIDTH = 1000;
    //The height of the window
    private int HEIGHT = 800;
    private static File inputFile;
    private static FileParser fileParser;
    private static TeamManager teamManager;
    private boolean importFile;
    private boolean choose;
    private boolean fieldViewer;
    private Title title;
    private ButtonManager buttonManager;
    private StateManager state;
    private FieldViewer fv;
    private PlayerViewer pv;
    private Texture background;
    private boolean textureLoaded;

    /**
     * This is the main method that calls the SoccerVisualization constructor
     * @param args an srray of Strings (in this case, not used)
     */
    public static void main(String[] args) {
        new SoccerVisualization();
    }

    /**
     * This is a SoccerVisualization constructor which creates a new
     *  SoccerVisualization instance.
     */
    public SoccerVisualization() {
        this.textureLoaded = false;

        //Create a GLCanvas
        GLCanvas canvas = new GLCanvas();

        //This class instance as the GLEventListener
        canvas.addGLEventListener(this);

        //Create an animator object useing the canvas
        FPSAnimator animator = new FPSAnimator(canvas, 40);

        //Create and show the game window/JFrame
        createJFrame(canvas, animator);

        //Give focus to the canvas
        canvas.setFocusable(true);

        //Start the game loop
        animator.start();


        state = new StateManager();

        buttonManager = new ButtonManager(state);

        teamManager = new TeamManager();
        
        fv = new FieldViewer(teamManager,state);

        pv = new PlayerViewer(teamManager,state);

        fileParser = new FileParser(teamManager,state);

        title = new Title();

        importFile = true;
        choose = false;
        fieldViewer = false;

        

        canvas.addMouseListener(this);
        canvas.addMouseMotionListener(this);
    }

    /**
     * Creates and displays a JFrame object from a given GLCanvas
     *  and Animator object.
     * @param canvas the canvas that the JFrame will display.
     * @param animator the Animator used by the canvas and JFrame.
     */
    private void createJFrame(GLCanvas canvas, final Animator animator) {
        //Create the JFrame and set the dimensions
        JFrame f = new JFrame(NAME);
        f.setSize(WIDTH, HEIGHT);

        JPanel buttonPanel = new JPanel();
        JButton fileSelection = new JButton("Add Data");
        fileSelection.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                System.out.println("inside createJFrame");
                chooseFileToParse();
            }
        });
        buttonPanel.add(fileSelection);

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
     //   panel.add(buttonPanel, BorderLayout.SOUTH);
        panel.add(canvas, BorderLayout.CENTER);
        //Add the given GLCanvas to the JFrame
        f.add(panel);
        //Add a new custom WindowListener
        f.addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent e) {
                animator.stop();
                System.exit(0);
            }
        });

        //Make the JFrame visible
        f.setVisible(true);
    }

    /**
     * Called by the GLAutoDrawable immediately after the OpenGL
     *  context is initialized for the first time.
     * @param drawable the drawable object to be referred to
     */
    public void init(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();

        //Set the clear color and drawing color to black
        gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        gl.glColor3f(0.0f, 0.0f, 0.0f);
        gl.glShadeModel(gl.GL_SMOOTH);
    }

    /**
     * Called to initiate the OpenGL rendering. It is also the method called
     *  by the Animator object to update the display.
     * @param drawable the drawable object to be referred to
     */
    public void display(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);

        if (state.choosingFile()) {
            System.out.println("inside display");
            chooseFileToParse();
        }

        if(!textureLoaded) {
            this.background = loadTexture("src/org/rowan/infovis/soccer/background.png");
            textureLoaded = true;
        }
        switch(state.getState()) {
            case 0:
                
                buttonManager.drawImport(gl);
                drawBackground(gl);
                              
                break;
            case 1:
                fv.draw(gl, drawable);
                buttonManager.drawPlayerViewer(gl);
                break;
            case 2:
                pv.draw(gl);
                buttonManager.drawFieldViewer(gl);
                buttonManager.drawSelection(gl);
                break;
            default: break;
        }

        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    private void drawBackground(GL gl) {
        gl.glColor3f(1f, 1f, 1f);

        TextureCoords tc = background.getImageTexCoords ();
        float tx1 = tc.left ();
        float ty1 = tc.top ();
        float tx2 = tc.right ();
        float ty2 = tc.bottom ();

        background.enable ();
        background.bind ();

        gl.glPushMatrix();
            gl.glBegin (GL.GL_QUADS);
                gl.glTexCoord2f (tx1, ty2);
                gl.glVertex2f (0.0f, 0.0f);
                gl.glTexCoord2f (tx2, ty2);
                gl.glVertex2f (1000.0f, 0f);
                gl.glTexCoord2f (tx2, ty1);
                gl.glVertex2f (1000.0f, 800.0f);
                gl.glTexCoord2f (tx1, ty1);
                gl.glVertex2f (0.0f, 800.0f);
            gl.glEnd ();
        gl.glPopMatrix();

       background.disable ();
    }
    /**
     * Called during the first repaint after the component is resized. This includes the
     * first time the component appears on the screen.
     * @param drawable the drawable object to be referred to
     * @param x the x position of the reshape
     * @param y the y position of the reshape
     * @param width the width of the new reshape
     * @param height the height of the new reshape
     */
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        GL gl = drawable.getGL();
        GLU glu = new GLU();

        //reset the WIDTH and HEIGHT fields
        WIDTH = width;
        HEIGHT = height;

        //reset the viewport to the new window size
        gl.glViewport(0, 0, WIDTH, HEIGHT);

        //reset the projection matrix
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluOrtho2D(0, WIDTH, 0, HEIGHT);
    }

    private Texture loadTexture(String filename) {
        Texture texture = null;
        try {
            texture = TextureIO.newTexture(new File(filename), false);
            texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
            texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
        } catch (Exception e) {
            System.out.println("error loading texture from " + filename + " " + e);
        }

        return texture;
    }

    /**
     * Called when the display mode or display device changes.
     * @param drawable
     * @param modeChanged whether the mode was changed or not
     * @param deviceChanged whether the device had been changed or not
     */
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
            boolean deviceChanged) {
    }

    public void actionPerformed(ActionEvent e) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    private void chooseFileToParse() {
        JFileChooser fileChooser = new JFileChooser(System.getProperty("user.dir"));
        JFrame frame = new JFrame();

        //JOptionPane.showMessageDialog(frame, "Choose a CSV file exported from MS Excel");

        int returnVal = fileChooser.showOpenDialog(frame);
        if (returnVal != JFileChooser.APPROVE_OPTION) {
            return;  //file selection was cancelled
        }
        //file was chosen, set to input
        inputFile = fileChooser.getSelectedFile();
        if (inputFile.getName().endsWith(".csv")) {
            try {
                state.setChoosingFile(false);
                fileParser.readFileIn(inputFile);
            } catch (Exception e) {
                System.out.println("There was an exception reading file: " + inputFile.getName() + " " + e);
            }
        } else {
            JOptionPane.showMessageDialog(frame, "File selected was not in CSV file format" + "\n" + "(Please export from MS Excel first)");
        }
    }

    public void mouseReleased(MouseEvent e) {
        int mouseX = e.getX();
        int mouseY = Math.abs(e.getY() - HEIGHT);

        buttonManager.mouseReleased(mouseX, mouseY);

        switch(state.getState()) {
            case 1: fv.mouseReleased(mouseX, mouseY); break;
            case 2: pv.mouseReleased(mouseX, mouseY); break;
            default: break;
        }
    }

    public void mouseMoved(MouseEvent e) {
        int mouseX = e.getX();
        int mouseY = Math.abs(e.getY() - HEIGHT);

        buttonManager.mouseMoved(mouseX, mouseY);

        switch(state.getState()) {
            case 1: fv.mouseMoved(mouseX, mouseY); break;
            case 2: pv.mouseMoved(mouseX, mouseY); break;
            default: break;
        }

    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseDragged(MouseEvent e) {
    }
}
